//
//  SimpleNetWork.swift
//  SimpleNetWork
//
//  Created by mac on 15/3/2.
//  Copyright (c) 2015年 gpr. All rights reserved.
//

import UIKit

///  请求的回调 closure
///
///  @param AnyObject? 请求结果
///  @param NSError?   错误信息
///
public typealias Complete = ( result: AnyObject?, error: NSError?)-> ()

///  用于多图片下载,的回调,当所有图片下载完毕之后才会调用此地址
///
///  @param String   图片的下载地址
///  @param UIImage? 下载完的图片, 如果下载失败返回空
///  @param NSError? 下载的错误信息
public typealias ImageTaskComplete = ([(urlString: String, image: UIImage?, error: NSError?)])-> ()

// 用于下载完毕的回调
typealias ImageDownLoadTuples = (urlString: String, image: UIImage?, error: NSError?)
typealias ImageDownLoadComplete = (urlString: String, localPath: String?, error: NSError?)

// MARK: 下载图片使用
extension SimpleNetWork {
    
    ///  下载单张图片
    ///
    ///  :param: urlString 图片的url 地址字符串
    ///  :param: complete  下载成功的回调
    func downLoadImageWithURLString( urlString: String, complete:(urlString: String, imagePath: String?, image: UIImage?, error: NSError? ) -> ()) {
        let info = imageExistWithURLString(urlString)
        if info.exist {
            complete(urlString: urlString, imagePath: info.imagePath, image: info.image, error: nil)
        } else {
            downLoadWithURL(urlString){ (imageData, error) -> () in
                var image: UIImage?
                var path: String?
                if error == nil {
                    let data = imageData as! NSData
                    data.writeToFile(info.imagePath, atomically:true)
                    image = UIImage(data: data)
                    path = info.imagePath
                }
                dispatch_async(dispatch_get_main_queue()) { () -> Void in
                     complete(urlString: urlString, imagePath: path, image: image, error: error)
                }
            }
        }
    }
    
    ///  下载获得多张图片,并缓存到本地,并返回所有图片,信息,注意该方法返回的是图片的缓存路径
    ///
    ///  :param: urlStrings 图片资源地址
    ///  :param: complete   成功的回调,当成功的时候同意返回
    func downLoadImages( urlStrings: [String], complete: ( ([ImageDownLoadComplete])-> () ) ) {
        var imageInfos = [ImageDownLoadComplete] ()
        let group = dispatch_group_create()
        let image :UIImage?
        var exist = true
        for itemURLString in urlStrings {
            // 先检查文件是否存在
            let info = imageExistWithURLString(itemURLString)
            if info.exist {
                let error: NSError? = nil
                imageInfos.append( (itemURLString, info.imagePath, error) )
            } else {
                exist = false
                let queue = dispatch_get_global_queue(0, 0)
                dispatch_group_async(group, queue) { () -> Void in
                    dispatch_group_enter(group)
                    // 下载图片
                    self.downLoadWithURL(itemURLString, complete: { (imageData, error) -> () in
                        var image: UIImage?
                        // 下载完毕缓存到本地
                        if imageData != nil {
                            let data = imageData as! NSData
                            data.writeToFile(info.imagePath, atomically:true)
                            image = UIImage(data: data)
                        }
                        // 跳出群组
                        imageInfos.append( (itemURLString, info.imagePath, error) )
                        dispatch_group_leave(group)
                    })
                }
            }
        }
        if exist {
            complete(imageInfos)
        } else {
            // 统一处理结果
            dispatch_group_notify(group, dispatch_get_main_queue()) {
                complete(imageInfos)
            }
        }
    }

    
    ///  下载获得多张图片,并缓存到本地,并返回所有图片,注意该方法由于返回所有图片所以比较消耗内存
    ///
    ///  :param: urlStrings 图片资源地址
    ///  :param: complete   成功的回调,当成功的时候同意返回
    func downLoadImages( urlStrings: [String], complete: ( ([ImageDownLoadTuples])-> () ) ) {
        var imageInfos = [ImageDownLoadTuples] ()
        let group = dispatch_group_create()
        let image :UIImage?
        for itemURLString in urlStrings {
            // 先检查文件是否存在
            let info = imageExistWithURLString(itemURLString)
            if info.exist {
                println("本地有 搞定 : " + info.imagePath)
                let error: NSError? = nil
                let item = (itemURLString, info.image, error)
                imageInfos.append( item )
            } else {
                let queue = dispatch_get_global_queue(0, 0)
                dispatch_group_async(group, queue) { () -> Void in
                    dispatch_group_enter(group)
                    // 下载图片
                    self.downLoadWithURL(itemURLString, complete: { (imageData, error) -> () in
                        var image: UIImage?
                        // 下载完毕缓存到本地
                        if imageData != nil {
                            let data = imageData as! NSData
                            data.writeToFile(info.imagePath, atomically:true)
                            image = UIImage(data: data)
                            println("下载完毕缓存到本地 : " + info.imagePath)
                        }
                        // 跳出群组
                        imageInfos.append( (itemURLString, image, error) )
                        dispatch_group_leave(group)
                    })
                }
            }
        }
        // 统一处理结果
        dispatch_group_notify(group, dispatch_get_main_queue()) { 
            complete(imageInfos)
        }
    }
    
    ///  根据传进来的 url地址检查本地是否有缓存,如果有连图片一块返回
    ///
    ///  :param: urlString 图片资源地址
    ///
    ///  :returns: (图片是否有本地缓存, 图片)
    func imageExistWithURLString( urlString: String ) -> ( exist: Bool, image: UIImage?, imagePath:String ) {
        let imagePath = imagesCacheDir.stringByAppendingPathComponent(urlString.md5)
        let exist = NSFileManager.defaultManager().fileExistsAtPath(imagePath)
        var image: UIImage?
        if exist {
            image = UIImage(contentsOfFile: imagePath)
        }
        return ( exist, image, imagePath )
    }
    
    func getImageFromLocalWithURLString(urlString: String) -> UIImage? {
        var obj: AnyObject? = imageCache.objectForKey(urlString)
        if obj != nil {
            let image = obj as! UIImage
            return image
        }
        let path = imagesCacheDir.stringByAppendingPathComponent(urlString.md5)
        let image = UIImage(contentsOfFile: path)
        if image != nil {
            let scale = UIScreen.mainScreen().scale
            let imageSize = image!.size.width * image!.size.height * scale
            imageCache.setObject(image!, forKey: urlString, cost: Int(imageSize))
        }
        return image
    }
}

public class SimpleNetWork {
    lazy var session: NSURLSession! = {
        return NSURLSession.sharedSession()
    }()
    private static let instance = SimpleNetWork()
    private static let DOMAIN_STR = "com.gpr.SimpleNetWork"
    private static let IMAGES_CACHE_DIR = "com.gpr.SimpleNetWork.imagesCache"
    var timeOut: NSTimeInterval = 15
    lazy private var imageCache: NSCache! = {
        var cache = NSCache()
        cache.totalCostLimit = 1024 * 1024 * 5
        return cache
    }()
    
    /// 单例构造方法
    public class var shareInstance: SimpleNetWork {
        return instance
    }
    
    // MARK: 文件缓存路径,用于下载图片
    lazy var imagesCacheDir: String! = {
        var cachePath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.CachesDirectory, NSSearchPathDomainMask.UserDomainMask, true).last as! String
        cachePath = cachePath.stringByAppendingPathComponent(IMAGES_CACHE_DIR)
        // 判断文件是否存在
        var isDirctionary: ObjCBool = true
        let exist = NSFileManager.defaultManager().fileExistsAtPath(cachePath, isDirectory: &isDirctionary)
        // 如果文件不存在,文件存在 && 不是文件夹就干掉重新创建
        if !exist || !isDirctionary {
            NSFileManager.defaultManager().removeItemAtPath(cachePath, error: nil)
            var error: NSError?
            NSFileManager.defaultManager().createDirectoryAtPath(cachePath, withIntermediateDirectories: false, attributes: nil, error: &error)
            if error != nil {
                println(error)
            }
        }
        return cachePath
    }()
    
    
    ///  根据传进来的地址来开启下载任务
    ///
    ///  :param: urlString 下载地址
    ///  :param: complete  成功的回调
    func downLoadWithURL( urlString: String, complete: Complete ) {
        let request = NSURLRequest(URL: NSURL(string: urlString)!, cachePolicy: NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData, timeoutInterval: timeOut)
        session.downloadTaskWithRequest( request ){ (locationURL, _, error) -> Void in
            var data:NSData?
            if error == nil {
                data = NSData(contentsOfURL: locationURL)
            }
            complete(result: data, error: error)
        }.resume()
    }
    
    ///  请求一段JSON数据
    ///
    ///  :param: urlString 请求地址字符串
    ///  :param: method    请求方法类型
    ///  :param: params    请求的参数
    ///  :param: complete  请求完成的回调
    public func requestJSON(var urlString: String, _ method: HTTPMethod, _ params: [String: AnyObject]?, _ complete: Complete?){
        // 解析参数
        let paramString = self.queryString(params)
        if paramString == nil && method == HTTPMethod.POST{
            let error = NSError(domain: SimpleNetWork.DOMAIN_STR, code: 0, userInfo: ["请求方法" : "请求参数为空但请求方法为POST"])
            self.completeOperation(complete, error,nil)
            return
        }
        let request = self.requestFromURLString(urlString ,method, paramString)
        if request == nil {
            let error = NSError(domain: SimpleNetWork.DOMAIN_STR, code: 0, userInfo: ["请求方法: \(method) 或者 请求参数\(params) 有误" : "请检查"])
            self.completeOperation(complete, error,nil)
            return
        }
        session.dataTaskWithRequest(request!, completionHandler: { (resultData, _, error) -> Void in
            if error != nil {
                self.completeOperation(complete, error,nil)
            }
            // 反序列化
            let json: AnyObject? = NSJSONSerialization.JSONObjectWithData(resultData, options: NSJSONReadingOptions.allZeros, error: nil)
            if json == nil {
                let error = NSError(domain: SimpleNetWork.DOMAIN_STR, code: 0, userInfo: ["error": "JSON反序列化失败"])
                self.completeOperation(complete, error,nil)
            } else {
                self.completeOperation(complete, nil,json)
            }
        }).resume()
    }
    
    ///  处理完成之后的操作
    ///
    ///  :param: complete 完成之后的操作 block
    ///  :param: error    操作的错误信息
    ///  :param: result   结果数据
    func completeOperation(complete: Complete? ,_ error: NSError?, _ result: AnyObject?) {
        if complete != nil {
            if complete == nil { return }
            if NSThread.currentThread().isMainThread {
                complete!(result: result, error: error)
            } else {
                dispatch_async(dispatch_get_main_queue(), { () -> Void in
                    complete!(result: result, error: error)
                })
            }
        }
        
    }
    
    ///  根据传进来的参数获得 NSURLRequest 对象
    ///
    ///  :param: urlString   参数地址字符串
    ///  :param: method      请求方式
    ///  :param: paramString 请求参数字符串
    ///
    ///  :returns: 请求
    func requestFromURLString(var urlString : String, _ method: HTTPMethod, _ paramString: String?) -> NSURLRequest?{
        if ( urlString.isEmpty || method.rawValue.isEmpty) {
            return nil
        }
        var request: NSMutableURLRequest? = nil
        if method == .GET {
            // 设置参数
            if paramString != nil {
                urlString = urlString + "?" + paramString!
            }
            request = NSMutableURLRequest(URL: NSURL(string: urlString)!, cachePolicy: NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData, timeoutInterval: timeOut)
        } else if method == .POST {
            request = NSMutableURLRequest(URL: NSURL(string: urlString)!, cachePolicy: NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData, timeoutInterval: timeOut)
            request?.HTTPMethod = method.rawValue
            // 设置参数
            request?.HTTPBody = paramString?.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)
        }
        return request
    }
    
    ///  根据传进来的参数字典获得参数字符串
    ///
    ///  :param: params 参数字典
    ///
    ///  :returns: 参数字符串
    func queryString(params: [String: AnyObject]?) -> String? {
        if params == nil { return nil }
        var result = [String]()
        for (k,v) in params! {
            let str = k + "=" + "\(v)".stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
            result.append(str)
        }
        return join("&", result)
    }
    
}

public enum HTTPMethod : String {
    case GET = "GET"
    case POST = "POST"
}
